﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace SimpleForm
{
    /// <summary>
    /// Enum type to specify the string that is to be segmented by Segmentor.
    /// </summary>
    /// EN: English;
    /// CHS: zh-CN;
    enum LANGUAGE { EN, CHS };

    class TestSegmentor
    {
        public static bool bIsDebugging = false;
    }

    class Segmentor
    {
        public Segmentor(string inputSentence)
        {
            StrSentence = inputSentence;
        }

        public Segmentor(string inputSentence, LANGUAGE lang)
        {
            StrSentence = inputSentence;
            Language = lang;
            InitializeSegmentor();
        }

        private HashSet<string> _dictionary = null;
        public HashSet<string> Dictionary
        {
            get { return _dictionary; }
        }
        private void InitializeSegmentor()
        {
            if (Language == LANGUAGE.CHS)
            {
                _dictionary = new HashSet<string>();
                using (StreamReader reader = new StreamReader(@"..\..\Resource\dict.txt", Encoding.Default))
                {
                    string singleLine  = "";
                    while (reader.EndOfStream != true)
                    {
                        singleLine = reader.ReadLine();
                        var tokens = singleLine.Split(new char[] { ' ', '\0' });
                        if (tokens.Count() == 2)
                        {
                            // The pattern of each line in dictionary file is:
                            // number(\0|' ')word; e.g. "12 Apple" or "12\0Android".
                            // Thus, we get the second token in tokens indicating the word.
                            _dictionary.Add(tokens[1]);
                        }
                    }
                }
                _dbgString += _dictionary.Count.ToString() + " items in the dictionary.\r\n";
            }
        }

        // Language of the sentence. Default language is EN.
        private LANGUAGE _language = LANGUAGE.EN;
        public LANGUAGE Language
        {
            get { return _language; }
            set 
            {
                if ((int)value >= (int)LANGUAGE.EN || (int)value <= (int)LANGUAGE.CHS)
                {
                    _language = value;
                }
                else
                {
                    // Do sth. to warning the language fault.
                }
            }
        }

        // String to store the input sentence to be segmented;
        private string _strSentence = "";
        public string StrSentence
        {
            get { return _strSentence; }
            set { _strSentence = value; }
        }

        // List of string to store words that are segmented from the _strSentence;
        private List<string> _segmentedList = null;
        public List<string> SegmentedList
        {
            get 
            {
                if (_segmentedList == null)
                {
                    // The sentence has not been segmented or no segmented result,
                    // we still try to segmented it once at least.
                    SegmentSentence();
                }

                // After segmentation, the _segmentedList could be something inside or
                // just null.
                return _segmentedList; 
            } 
        }

        // List of characters or words that we don't recognize.
        private List<string> _unrecognizedList = new List<string>();
        public List<string> UnrecognizedList
        {
            get { return _unrecognizedList; }
        }

        private void SegmentSentence()
        {
            // Clear the segmentedList first.
            if (_segmentedList == null)
            {
                _segmentedList = new List<string>();
            }
            Debug.Assert(_segmentedList != null);
            _segmentedList.Clear();

            // Do sth. to segment the string.
            if (Language == LANGUAGE.EN)
            {
                // For English sentence, we separate it with blank.
                if (_strSentence.Length > 0)
                {
                    var splittedList = _strSentence.Split(" ".ToArray());
                    if (splittedList != null)
                    {
                        _segmentedList = splittedList.ToList<string>();
                    }
                    else
                    {
                        // Sets the segmented list to be null.
                        _segmentedList = null;
                    }
                }
            }
            else if (Language == LANGUAGE.CHS)
            {
                // For simple Chinese, we separate it according to the dictionary.
                // Dictionary must have been initialized before we use it.
                Debug.Assert(Dictionary != null);

                // Before being tokenized, the sentence needs to be separate by punctuations
                // in it first.
                string punctuactions = @"，。《》、？；：’“”、|】}【{=+——-（）*&……%￥#@！~";
                string[] sentenceParts = StrSentence.Split(punctuactions.ToArray());
                foreach (string part in sentenceParts)
                {
                    // We tokenize each part of the sentence separated by punctuations.
                    // We wish to match words in dictionary as long as we can.
                    int index = 0;
                    while (index < part.Length)
                    {
                        // 1 character of Chinese is the smallest unit for a Chinese word.
                        int maxLength = 1;
                        string word = part.Substring(index, maxLength);

                        bool bCanWeRecognize = false;
                        // Start from a specific index, we stretch the word's length to match.
                        for (int length = 1; index + length <= part.Length; length++)
                        {
                            string possibleWord = part.Substring(index, length);
                            if (Dictionary.Contains(possibleWord) == true)
                            {
                                bCanWeRecognize = true;
                                word = possibleWord;
                                maxLength = length;
                            }
                        }

                        if (bCanWeRecognize == false)
                        {
                            // If we can't recognize the character, we mark the character down.
                            _unrecognizedList.Add(word);
                        }
                        _segmentedList.Add(word);
                        index += maxLength;
                    }
                }
            }
            else
            {
                // Other Language...
            }

            // Fills debugging information.
            if (TestSegmentor.bIsDebugging)
            {
                _dbgString = "language: " + Language.ToString() + ";\r\n";
                _dbgString += "sentence: " + StrSentence + "\r\n";
                if (SegmentedList != null)
                {
                    _dbgString += "segmented list: \r\n";
                    foreach (string word in SegmentedList)
                        _dbgString += word + "; ";
                    _dbgString += "\r\n";
                }
                else
                {
                    _dbgString += "No segmentation result.\r\n";
                }
            }
        }

        private string _dbgString = "";
        public string DebugString
        {
            get { return "====debug string====\r\n" + _dbgString + "====================\r\n"; }
        }
    }
}
